#include "scheduler.h"
#include "log.h"
#include "macro.h"
#include "hook.h"
#include <string>

namespace framework
{
static framework::Logger::ptr g_logger = FRAMEWORK_LOG_NAME("system");

//协程调度器的指针    作用：在线程中获取当前协程的协程ID
static thread_local Scheduler* t_scheduler = nullptr;
//当前线程主协程的指针
static thread_local Fiber* t_scheduler_fiber = nullptr;


//线程池默认线程数量
//是否使用当前调用线程
//协程调度器名称
Scheduler::Scheduler(size_t threads, bool use_calle, const std::string& name):m_name(name)
{
    FRAMEWORK_ASSERT(threads > 0);

    //如果使用当前线程调用协程
    if(use_calle)
    {
        //如果当前线程没有协程，就创建一个主协程
        framework::Fiber::GetThis();
        --threads;

        FRAMEWORK_ASSERT(GetThis() == nullptr);  //一个线程只能有一个协程调度器
        t_scheduler = this;

        //这里必须绑定this指针，否则Fiber类中不知道调用那个Scheduler类的run函数
        m_rootFiber.reset(new Fiber(std::bind(&Scheduler::run, this), 0, true));
        framework::Thread::SetName(m_name);

        t_scheduler_fiber = m_rootFiber.get();
        m_rootThread = framework::GetThreadId();
        m_threadIds.push_back(m_rootThread);
    }
    else
    {
        m_rootThread = -1;
    }
    m_threadCount = threads;
}

Scheduler::~Scheduler()
{
    FRAMEWORK_ASSERT(m_stopping);  //断言当前线程已经停止
    if(GetThis() == this)
    {
        t_scheduler = nullptr;
    }
}


//启动协程调度器
void Scheduler::start()
{
    MutexType::Lock lock(m_mutex);
    if(!m_stopping) //如果线程正在运行
    {
        return;
    }
    m_stopping = false;

    FRAMEWORK_ASSERT(m_threads.empty()); //断言线程池为空

    //给线程池添加线程
    m_threads.resize(m_threadCount);
    for(size_t i = 0; i < m_threadCount; ++i)
    {
        std::string name =  m_name + "_" + std::to_string(i);
        m_threads[i].reset(new Thread(std::bind(&Scheduler::run, this), name));
        m_threadIds.push_back(m_threads[i]->getId()); //线程构造函数加信号量的作用，确保可以拿到线程id
    }
}

//停止协程调度器
void Scheduler::stop()
{
    m_autoStop = true;
    if(m_rootFiber  //使用了调度协程（use_calle == true）
        && m_threadCount == 0  //线程池中的线程数量为0
        && (m_rootFiber->getState() == Fiber::TERM || m_rootFiber->getState() == Fiber::INIT)) //调度协程的状态为结束或初始化
    {
        FRAMEWORK_LOG_INFO(g_logger) << this << " stopped";
        m_stopping = true; //设置线程状态为停止

        if(stopping()) //如果可以停止
        {
            return;
        }
    }

    if(m_rootThread != -1) //如果使用当前线程调用协程
    {
        FRAMEWORK_ASSERT(GetThis() == this);
    }
    else
    {
        FRAMEWORK_ASSERT(GetThis() != this);
    }

    m_stopping = true;

    //唤醒线程池中所有的线程，使其执行完自动结束
    for(size_t i = 0; i < m_threadCount; ++i)
    {
        tickle();
    }

    if(m_rootFiber)
    {
        tickle();  //让调度线程执行完自动结束
    }

    //协程调度器使用了当前线程
    if(m_rootFiber)
    {
        if(!stopping()) //如果协程调度器不可以停止
        {
            m_rootFiber->call();  //执行协程调度器自己的协程
        }
    }

    std::vector<Thread::ptr> thrs;
    {
        MutexType::Lock lock(m_mutex);
        thrs.swap(m_threads);
    }

    for(auto& i: thrs)
    {
        i->join();
    }
}

//协程调度函数
void Scheduler::run()
{
    FRAMEWORK_LOG_DEBUG(g_logger) << m_name << " run";
    set_hook_enable(true); 
    setThis();  //设置当前的协程调度器为自己

    //当前线程id != 协程调度器的调度线程（主线程id）
    if(framework::GetThreadId() != m_rootThread) 
    {
        //主协程（调度协程）= 当前运行的协程
        t_scheduler_fiber = Fiber::GetThis().get();  //新线程会创建主协程
    }

    //当调度任务全部完成之后只需idle_fiber
    Fiber::ptr idle_fiber(new Fiber(std::bind(&Scheduler::idle, this)));
    Fiber::ptr cb_fiber;

    FiberAndThread ft;
    while(true)
    {
        ft.reset();
        bool tickle_me = false;
        bool is_active = false;  //是否空闲(当前是否有任务)
        {
            Mutex::Lock lock(m_mutex);

            //遍历协程队列
            auto it = m_fibers.begin();
            while (it != m_fibers.end())
            {
                //指定执行的线程id不为当前线程id
                if(it->thread != -1 && it->thread != framework::GetThreadId())
                {
                    ++it;
                    tickle_me = true; //通知其他线程处理这个协程
                    continue;
                }

                FRAMEWORK_ASSERT(it->fiber || it->cb);
                //协程当前状态为执行中时跳过该协程
                if(it->fiber && it->fiber->getState() == Fiber::EXEC)
                {
                    ++it;
                    continue;
                }

                ft = *it;
                m_fibers.erase(it++);
                ++m_activeThreadCount; //工作线程数量
                is_active = true;
                break;
            }

            tickle_me |= it != m_fibers.end();
        }

        //通知其他线程处理协程
        if(tickle_me)
        {
            tickle(); //通知协程调度器有任务了
        }

        //有协程
        if(ft.fiber && (ft.fiber->getState() != Fiber::TERM    //结束
                    && ft.fiber->getState() != Fiber::EXCEPT)) //异常
        {
            ft.fiber->swapIn(); //将当前协程切换到运行状态
            --m_activeThreadCount;

            //执行完成之后
            if(ft.fiber->getState() == Fiber::READY)
            {
                schedule(ft.fiber); //将协程再次放到任务队列
            }
            else if(ft.fiber->getState() != Fiber::TERM
                    && ft.fiber->getState() != Fiber::EXCEPT)  //当前状态为执行中时
            {
                ft.fiber->m_state = Fiber::HOLD; // 暂停
            }

            ft.reset(); //重置数据
        }
        else if(ft.cb)  //有协程执行函数
        {
            if(cb_fiber)
            {
                cb_fiber->reset(ft.cb);  //协程的reset
            }
            else
            {
                //智能指针的reset
                cb_fiber.reset(new Fiber(ft.cb)); //重置协程执行函数，并设置状态
            }
            ft.reset();

            cb_fiber->swapIn();//将当前协程切换到运行状态
            --m_activeThreadCount;
            if(cb_fiber->getState() == Fiber::READY)
            {
                schedule(cb_fiber);
                cb_fiber.reset();
            }
            else if(cb_fiber->getState() == Fiber::EXCEPT    //异常
                    || cb_fiber->getState() == Fiber::TERM)  //结束
            {
                cb_fiber->reset(nullptr);
            }
            else
            {//if(cb_fiber->getState() != Fiber::TERM) {
                cb_fiber->m_state = Fiber::HOLD; // 暂停
                cb_fiber.reset(); 
            }
        }
        else  //当没有ft.fiber 和 ft.cb 时就表示没有任务做了
        {
            if(is_active)
            {
                --m_activeThreadCount;
                continue;
            }
            if(idle_fiber->getState() == Fiber::TERM)//结束
            {
                FRAMEWORK_LOG_INFO(g_logger) << "idle fiber term";
                break;
            }
        }

        ++m_idleThreadCount; //空闲线程数量
        idle_fiber->swapIn();
        --m_idleThreadCount;
        //协程无任务时可调度执行idle协程
        if(idle_fiber->getState() != Fiber::TERM && idle_fiber->getState() != Fiber::EXCEPT)
        {
            idle_fiber->m_state = Fiber::HOLD;
        }
    }
}

//设置当前的协程调度器
void Scheduler::setThis()
{
    t_scheduler = this;  
}



//通知协程调度器有任务了
void Scheduler::tickle()
{
    FRAMEWORK_LOG_INFO(g_logger) << "tickle";
}

//返回是否可以停止
bool Scheduler::stopping()
{
    MutexType::Lock lock(m_mutex);
    return m_autoStop && m_stopping  //是否自动停止  当前是否停止
        && m_fibers.empty() && m_activeThreadCount == 0; //待执行的协程队列为空  工作线程数量为零
}

//协程无任务时可调度执行idle协程
void Scheduler::idle()
{
    FRAMEWORK_LOG_INFO(g_logger) << "idle";

    //当不可以停止时
    while(!stopping())
    {
        //将当前协程切换到后台,并设置为HOLD状态
        framework::Fiber::YieldToHold();
    }
}


//=================== static ======================

//返回当前协程调度器
Scheduler* Scheduler::GetThis()
{
    return t_scheduler;
}

//返回当前协程调度器的调度协程
Fiber* Scheduler::GetMainFiber()
{
    return t_scheduler_fiber;
}

}

